#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>

#include <linux/pwm.h>
#include <linux/uaccess.h>

static int sg90_open(struct inode *inode, struct file *filp);
static ssize_t sg90_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt);
static ssize_t sg90_write(struct file *filp, const char __user *buf, size_t size, loff_t *offt);
static int sg90_release(struct inode *inode, struct file *filp);
int sg90_probe(struct platform_device *);
int sg90_remove(struct platform_device *);


/*********************************************************************************************/

#define sg90_CNT   1	/* 设备号个数 */
#define sg90_NAME  "sg90"	/* 名字 */
/*********************************************************************************************/



/*********************************************************************************************/
 /* sg90 设备结构体 */
 struct sg90_dev{
	dev_t devid; /* 设备号 */
	int major;
	int minor;


	struct cdev cdev; /* cdev */
	struct class *class; /* 类 */
	struct device *device; /* 设备 */
	struct device_node *nd; /* 设备节点 */

	struct pwm_device *pwm;
 };

 struct sg90_dev sg90; /* sg90 设备 */



const struct of_device_id sg90_match_table[] = {
	{.compatible = "sg90"},
	{}
};

struct platform_driver sg90_platform_driver={
	.driver ={
		.name = "sg90",
		.of_match_table = sg90_match_table,
	},
	.remove = sg90_remove,
	.probe = sg90_probe,
};

/*********************************************************************************************/



/*********************************************************************************************/
/* 设备操作函数 */
static struct file_operations sg90_fops = {
	.owner = THIS_MODULE,
	.open = sg90_open,
	.read = sg90_read,
	.write = sg90_write,
	.release = 	sg90_release,
};
/*********************************************************************************************/


int sg90_probe(struct platform_device *dev)
{
	int ret = 0;


	/* 注册字符设备驱动 */
	/* 1、创建设备号 */	
	ret = alloc_chrdev_region(&sg90.devid, 0, sg90_CNT, sg90_NAME);	/* 申请设备号 */
	if(ret<0){
		printk("[ERROR] alloc_chrdev_region failed\n");
	}
	printk("[OK] This is sg90_probe\n");
	
	/* 2、初始化cdev */
	sg90.cdev.owner = THIS_MODULE;
	cdev_init(&sg90.cdev, &sg90_fops);
	
	/* 3、添加一个cdev */
	ret = cdev_add(&sg90.cdev, sg90.devid, sg90_CNT);
		
	/* 4、创建类 */
	sg90.class = class_create(THIS_MODULE, sg90_NAME);


	/* 5、创建设备 */
	sg90.device = device_create(sg90.class, NULL, sg90.devid, NULL, sg90_NAME);

	/*6. 获取PWM结合体 */
	sg90.pwm = devm_of_pwm_get(&dev->dev,dev->dev.of_node,NULL);
	if(IS_ERR(sg90.pwm)){
		printk("[ERROR] devm_of_pwm_get failed\n");
		return -1;
	}
	
	return ret;
}
int sg90_remove(struct platform_device *dev)
{

	return 0;
}


/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件，file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int sg90_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &sg90; /* 设置私有数据 */

	pwm_config(sg90.pwm,500000,20000000);
	pwm_set_polarity(sg90.pwm,PWM_POLARITY_NORMAL);
	pwm_enable(sg90.pwm);
	printk("[OK] sg90_open\n");
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t sg90_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{

	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件，表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数，如果为负值，表示写入失败
 */
static ssize_t sg90_write(struct file *filp, const char __user *buf, size_t size, loff_t *offt)
{
	int ret=0;
	unsigned char data[1];
	ret = copy_from_user(data,buf,size);
	pwm_config(sg90.pwm,500000+data[0]*100000/9,20000000);
	printk("[OK] sg90_write\n");
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int sg90_release(struct inode *inode, struct file *filp)
{
	pwm_config(sg90.pwm,500000,20000000);
	pwm_free(sg90.pwm);
	printk("[OK] sg90_release\n");
	return 0;
}


/*
 * @description	: 驱动入口函数 
 * @param 		: 无
 * @return 		: 0 成功;其他 失败
 */
static int __init sg90_init(void)
{
	int ret = 0;

	/* 6、注册platform设备 */
	platform_driver_register(&sg90_platform_driver);
	
	return ret;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit sg90_exit(void)
{
	/*  1. 删除cdev */
	cdev_del(&sg90.cdev);
	/* 2. 注销设备号 */
	unregister_chrdev_region(sg90.devid, sg90_CNT); 
	/* 3. 注销设备 */
	device_destroy(sg90.class, sg90.devid);
	/* 4. 注销类 */
	class_destroy(sg90.class);
	/* 5. 注销platform设备 */
	platform_driver_unregister(&sg90_platform_driver);
}

/* 
 * 将上面两个函数指定为驱动的入口和出口函数 
 */
module_init(sg90_init);
module_exit(sg90_exit);

/* 
 * LICENSE和作者信息
 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("fangjunjie");